Applied Methods · MA Public Administration
2026-10-02
This session develops the course strand on Data visualisation in R.
The aim is not to memorize all possible plots.
The aim is to learn how to choose an appropriate visualisation depending on:
Session 2 on data manipulation
We saw how to:
summary(), glimpse(), head/tail())%>%, mutate())case_when(), ifelse())A sytaxe exemple to recode data
Visual communication is more accessible than raw numbers.
Help to communicate simple message with a handful of visuals.
More easily understandable by wider audience.
Oftentimes, provide a clear summary of the visualisation.
Choices are made, and these are not random.
Data visualization convey a message
Depending on how we present the visualization, convey different message.
Subject to manipulation.
Changing levels of aggregation conveys a different message.
Changing the surface plotter changes the message.
Other things we should look out for?
Six representation of the exact same data, different message.
Important to be critical on how data is represented.
Visualizing data summarizes the data.
How to recognize figures that do not read well?
Labeling axis
Conventional order
Representative axis scales
Limit the use of colors and shape (when possible)
Before writing code, ask:
What do I want to show?
Then ask:
| Situation | Typical plot |
|---|---|
| One categorical variable | Bar plot |
| One continuous variable | Histogram, density, boxplot |
| Continuous × continuous | Scatterplot |
| Categorical × continuous | Boxplot, violin, jitter |
| Categorical × categorical | Grouped/stacked bars, tile plot |
| Time × continuous | Line plot |
We use municipal data on direct democratic votes
Each row is one municipality-ballot combinasion.
Rows: 1,099,161
Columns: 20
$ DateNameDE <chr> "2026-06-14 Änderung des Bundesgesetzes über den zivil…
$ DateNameFR <chr> "2026-06-14 Modification de la loi fédérale sur le ser…
$ PlaceDE <chr> "......Aeugst am Albis", "......Aeugst am Albis", "...…
$ PlaceFR <chr> "......Aeugst am Albis", "......Aeugst am Albis", "...…
$ NumberMun <chr> "0001", "0001", "0001", "0001", "0001", "0001", "0001"…
$ anr <chr> "6870", "6860", "6850", "6840", "6830", "6823", "6822"…
$ YesPercent <dbl> 58.50410, 49.20949, 50.21053, 23.79455, 38.08533, 34.2…
$ YesVote <dbl> 571, 498, 477, 227, 366, 317, 708, 418, 123, 156, 459,…
$ NoVote <dbl> 405, 514, 473, 727, 595, 609, 223, 527, 648, 610, 413,…
$ Voters <dbl> 1381, 1381, 1388, 1388, 1388, 1388, 1388, 1388, 1370, …
$ ReceivedBallots <dbl> 994, 1017, 959, 965, 966, 956, 956, 956, 773, 771, 881…
$ Participation <dbl> 71.97683, 73.64229, 69.09222, 69.52450, 69.59654, 68.8…
$ ValidBallots <dbl> 976, 1012, 950, 954, 961, 952, 952, 952, 771, 766, 872…
$ PlaceType <chr> "Municipality", "Municipality", "Municipality", "Munic…
$ Date <date> 2026-06-14, 2026-06-14, 2026-03-08, 2026-03-08, 2026-…
$ Year <dbl> 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2025, …
$ TitleDE <chr> " Änderung des Bundesgesetzes über den zivilen Ersatzd…
$ TitleFR <chr> " Modification de la loi fédérale sur le service civil…
$ anr.Swissvotes <dbl> 687.0, 686.0, 685.0, 684.0, 683.0, 682.3, 682.2, 682.1…
$ GDENR <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
We also will use the Swissvotes data.
Each row is a ballot
Each column contains information on the ballot
You can access the codebook here
For some visualisations, we use only one ballot.
ggplot2A ggplot object is built step by step.
Main components:
aes() versus fixed propertiesInside aes(): map a variable to a visual feature.
The aes() stands for aestetics.
Outside aes(): set one fixed visual property.
geomYou can also specify data in the geom directly.
This can be useful if you want to plot different data in the same plot or different aestetics in the same plot
You can assign the plot to an object, then add properties to this object
| Situation | Typical plot | Useful geom |
|---|---|---|
| One categorical variable | Bar plot | geom_bar() |
| One continuous variable | Histogram | geom_histogram() |
| One continuous variable | Density | geom_density() |
| One continuous variable | Boxplot | geom_boxplot() |
For one categorical variable, we usually show counts or proportions.
Most common option:
Long category labels are often easier to read horizontally.
data_swissvotes[is.na(data_swissvotes$institution)==F,] %>%
count(institution) %>%
ggplot(aes(x = reorder(institution, n), y = n)) +
geom_col(width=.5,
color = "black",
fill="light gray") +
coord_flip() +
theme_minimal() +
labs(
title = "Number of populare ballot by institution type",
x = "Institution",
y = "Number of ballots"
)For one continuous variable, we usually show a distribution.
Useful options:
You can change the scale of the axis with continuous variables.
When a variable is very skewed, a log scale can help.
ggplot2 has build in scale_x_log10 or scale_y_log10 function that do this for you.
When we have two variables, the choice of plot depends on the type of each variable.
| Variable 1 | Variable 2 | Typical plot |
|---|---|---|
| Continuous | Continuous | Scatterplot |
| Categorical | Continuous | Boxplot, violin, jitter |
| Categorical | Categorical | Bar plot, tile plot |
| Time | Continuous | Line plot |
We often use a scatterplot.
You can add a linear (or other relation) trend with geom_smooth()
A boxplot compares a continuous variable across groups.
ggplot(data_swissvotes[is.na(data_swissvotes$institution)==F,],
aes(x = institution,
y = volkja.proz)) +
geom_jitter(width = .15, alpha = .6) +
theme_minimal() +
labs(
title = "Distribution of ballot support by institution",
subtitle = "Each point represents a municipality",
x = "Direct democratic institution",
y = "Populare support"
)ggplot(data_swissvotes[is.na(data_swissvotes$institution)==F,],
aes(x = institution,
y = volkja.proz)) +
geom_boxplot(outlier.shape = NA) +
geom_jitter(width = .15, alpha = .5) +
theme_minimal() +
labs(
title = "Distribution of ballot support by institution",
x = "Direct democratic institution",
y = "Populare support"
)Grouped bar plot.
ggplot(data_swissvotes[is.na(data_swissvotes$coalition)==F &
is.na(data_swissvotes$institution)==F,],
aes(x =institution,
fill = coalition)) +
geom_bar(position = "dodge") +
theme_minimal() +
labs(
title = "Distribution of coalition across direct democratic institutions",
x = "Direct democratic institution",
y = "Number of ballots",
fill = "Coalition"
)ggplot(data_swissvotes[is.na(data_swissvotes$coalition)==F &
is.na(data_swissvotes$institution)==F,],
aes(x =institution,
fill = coalition)) +
geom_bar(position = "fill") +
scale_y_continuous() +
theme_minimal() +
labs(
title = "Distribution of coalition across direct democratic institutions",
x = "Direct democratic institution",
y = "Proportion of of ballots",
fill = "Coalition"
)data_swissvotes[is.na(data_swissvotes$coalition)==F &
is.na(data_swissvotes$institution)==F,] %>%
count(institution, coalition) %>%
ggplot(aes(x = institution,
y = coalition,
fill = n)) +
geom_tile(color = "white") +
theme_minimal() +
labs(
title = "Distribution of coalition across direct democratic institutions",
x = "Direct democratic institution",
y = "Coalition",
fill = "N"
)Line plots are useful for trends over time.
When there are several lines, use group.
data_mun %>%
group_by(Date, GDENR) %>%
summarize(part = mean(Participation)) %>%
filter(GDENR %in% c(1:20)) %>%
ggplot(aes(x = Date,
y =part,
group=GDENR)) +
geom_line(linewidth=.3) +
theme_minimal() +
labs(
title = "Participationin popular ballot in 20 municipalities over-time.",
x = "Time",
y = "Participation"
)data_mun %>%
group_by(Date, GDENR) %>%
summarize(part = mean(Participation)) %>%
filter(GDENR %in% c(1:10)) %>%
ggplot(aes(x = Date,
y =part,
color=factor(GDENR))) +
geom_line() +
theme_minimal() +
labs(
title = "Participationin popular ballot in 10 municipalities over-time.",
x = "Time",
y = "Participation",
fill = "Municipality number"
)One variable on the X axis, one on the Y axis
Other variables can be represented with different aestetics components
ggplot(data_swissvotes[is.na(data_swissvotes$coalition)==F &
is.na(data_swissvotes$institution)==F,],
aes(x=institution, y=gov.supp, color = coalition))+
geom_boxplot()+
labs(title = "Share of support by institution",
x="Institution",
y="National support of the government's position",
color = "Coalition")ggplot(data_swissvotes[is.na(data_swissvotes$coalition)==F &
is.na(data_swissvotes$institution)==F,],
aes(x=institution, y=gov.supp, color = coalition))+
geom_boxplot()+
labs(title = "Share of support by institution",
x="Time",
y="National support",
color = "Coalition")+
scale_color_manual(values=c("red", "blue", "grey", "black"))+
theme(legend.position = "bottom")The fill option works the same way as the color.
Instead of modifying the outline, you change the colors that fills the shape
ggplot(data_swissvotes[is.na(data_swissvotes$coalition)==F &
is.na(data_swissvotes$institution)==F,],
aes(x=institution, y=gov.supp, fill = coalition))+
geom_boxplot()+
labs(title = "Share of support by institution",
x="Time",
y="National support",
fill = "Coalition")+
scale_fill_manual(values=c("red", "blue", "grey", "black"))+
theme(legend.position = "bottom")You can set the colors with a default list of colors name as we did until now.
The full list of colors is available in R:
[1] "white" "aliceblue" "antiquewhite"
[4] "antiquewhite1" "antiquewhite2" "antiquewhite3"
[7] "antiquewhite4" "aquamarine" "aquamarine1"
[10] "aquamarine2" "aquamarine3" "aquamarine4"
[13] "azure" "azure1" "azure2"
[16] "azure3" "azure4" "beige"
[19] "bisque" "bisque1" "bisque2"
[22] "bisque3" "bisque4" "black"
[25] "blanchedalmond" "blue" "blue1"
[28] "blue2" "blue3" "blue4"
[31] "blueviolet" "brown" "brown1"
[34] "brown2" "brown3" "brown4"
[37] "burlywood" "burlywood1" "burlywood2"
[40] "burlywood3" "burlywood4" "cadetblue"
[43] "cadetblue1" "cadetblue2" "cadetblue3"
[46] "cadetblue4" "chartreuse" "chartreuse1"
[49] "chartreuse2" "chartreuse3" "chartreuse4"
[52] "chocolate" "chocolate1" "chocolate2"
[55] "chocolate3" "chocolate4" "coral"
[58] "coral1" "coral2" "coral3"
[61] "coral4" "cornflowerblue" "cornsilk"
[64] "cornsilk1" "cornsilk2" "cornsilk3"
[67] "cornsilk4" "cyan" "cyan1"
[70] "cyan2" "cyan3" "cyan4"
[73] "darkblue" "darkcyan" "darkgoldenrod"
[76] "darkgoldenrod1" "darkgoldenrod2" "darkgoldenrod3"
[79] "darkgoldenrod4" "darkgray" "darkgreen"
[82] "darkgrey" "darkkhaki" "darkmagenta"
[85] "darkolivegreen" "darkolivegreen1" "darkolivegreen2"
[88] "darkolivegreen3" "darkolivegreen4" "darkorange"
[91] "darkorange1" "darkorange2" "darkorange3"
[94] "darkorange4" "darkorchid" "darkorchid1"
[97] "darkorchid2" "darkorchid3" "darkorchid4"
[100] "darkred" "darksalmon" "darkseagreen"
[103] "darkseagreen1" "darkseagreen2" "darkseagreen3"
[106] "darkseagreen4" "darkslateblue" "darkslategray"
[109] "darkslategray1" "darkslategray2" "darkslategray3"
[112] "darkslategray4" "darkslategrey" "darkturquoise"
[115] "darkviolet" "deeppink" "deeppink1"
[118] "deeppink2" "deeppink3" "deeppink4"
[121] "deepskyblue" "deepskyblue1" "deepskyblue2"
[124] "deepskyblue3" "deepskyblue4" "dimgray"
[127] "dimgrey" "dodgerblue" "dodgerblue1"
[130] "dodgerblue2" "dodgerblue3" "dodgerblue4"
[133] "firebrick" "firebrick1" "firebrick2"
[136] "firebrick3" "firebrick4" "floralwhite"
[139] "forestgreen" "gainsboro" "ghostwhite"
[142] "gold" "gold1" "gold2"
[145] "gold3" "gold4" "goldenrod"
[148] "goldenrod1" "goldenrod2" "goldenrod3"
[151] "goldenrod4" "gray" "gray0"
[154] "gray1" "gray2" "gray3"
[157] "gray4" "gray5" "gray6"
[160] "gray7" "gray8" "gray9"
[163] "gray10" "gray11" "gray12"
[166] "gray13" "gray14" "gray15"
[169] "gray16" "gray17" "gray18"
[172] "gray19" "gray20" "gray21"
[175] "gray22" "gray23" "gray24"
[178] "gray25" "gray26" "gray27"
[181] "gray28" "gray29" "gray30"
[184] "gray31" "gray32" "gray33"
[187] "gray34" "gray35" "gray36"
[190] "gray37" "gray38" "gray39"
[193] "gray40" "gray41" "gray42"
[196] "gray43" "gray44" "gray45"
[199] "gray46" "gray47" "gray48"
[202] "gray49" "gray50" "gray51"
[205] "gray52" "gray53" "gray54"
[208] "gray55" "gray56" "gray57"
[211] "gray58" "gray59" "gray60"
[214] "gray61" "gray62" "gray63"
[217] "gray64" "gray65" "gray66"
[220] "gray67" "gray68" "gray69"
[223] "gray70" "gray71" "gray72"
[226] "gray73" "gray74" "gray75"
[229] "gray76" "gray77" "gray78"
[232] "gray79" "gray80" "gray81"
[235] "gray82" "gray83" "gray84"
[238] "gray85" "gray86" "gray87"
[241] "gray88" "gray89" "gray90"
[244] "gray91" "gray92" "gray93"
[247] "gray94" "gray95" "gray96"
[250] "gray97" "gray98" "gray99"
[253] "gray100" "green" "green1"
[256] "green2" "green3" "green4"
[259] "greenyellow" "grey" "grey0"
[262] "grey1" "grey2" "grey3"
[265] "grey4" "grey5" "grey6"
[268] "grey7" "grey8" "grey9"
[271] "grey10" "grey11" "grey12"
[274] "grey13" "grey14" "grey15"
[277] "grey16" "grey17" "grey18"
[280] "grey19" "grey20" "grey21"
[283] "grey22" "grey23" "grey24"
[286] "grey25" "grey26" "grey27"
[289] "grey28" "grey29" "grey30"
[292] "grey31" "grey32" "grey33"
[295] "grey34" "grey35" "grey36"
[298] "grey37" "grey38" "grey39"
[301] "grey40" "grey41" "grey42"
[304] "grey43" "grey44" "grey45"
[307] "grey46" "grey47" "grey48"
[310] "grey49" "grey50" "grey51"
[313] "grey52" "grey53" "grey54"
[316] "grey55" "grey56" "grey57"
[319] "grey58" "grey59" "grey60"
[322] "grey61" "grey62" "grey63"
[325] "grey64" "grey65" "grey66"
[328] "grey67" "grey68" "grey69"
[331] "grey70" "grey71" "grey72"
[334] "grey73" "grey74" "grey75"
[337] "grey76" "grey77" "grey78"
[340] "grey79" "grey80" "grey81"
[343] "grey82" "grey83" "grey84"
[346] "grey85" "grey86" "grey87"
[349] "grey88" "grey89" "grey90"
[352] "grey91" "grey92" "grey93"
[355] "grey94" "grey95" "grey96"
[358] "grey97" "grey98" "grey99"
[361] "grey100" "honeydew" "honeydew1"
[364] "honeydew2" "honeydew3" "honeydew4"
[367] "hotpink" "hotpink1" "hotpink2"
[370] "hotpink3" "hotpink4" "indianred"
[373] "indianred1" "indianred2" "indianred3"
[376] "indianred4" "ivory" "ivory1"
[379] "ivory2" "ivory3" "ivory4"
[382] "khaki" "khaki1" "khaki2"
[385] "khaki3" "khaki4" "lavender"
[388] "lavenderblush" "lavenderblush1" "lavenderblush2"
[391] "lavenderblush3" "lavenderblush4" "lawngreen"
[394] "lemonchiffon" "lemonchiffon1" "lemonchiffon2"
[397] "lemonchiffon3" "lemonchiffon4" "lightblue"
[400] "lightblue1" "lightblue2" "lightblue3"
[403] "lightblue4" "lightcoral" "lightcyan"
[406] "lightcyan1" "lightcyan2" "lightcyan3"
[409] "lightcyan4" "lightgoldenrod" "lightgoldenrod1"
[412] "lightgoldenrod2" "lightgoldenrod3" "lightgoldenrod4"
[415] "lightgoldenrodyellow" "lightgray" "lightgreen"
[418] "lightgrey" "lightpink" "lightpink1"
[421] "lightpink2" "lightpink3" "lightpink4"
[424] "lightsalmon" "lightsalmon1" "lightsalmon2"
[427] "lightsalmon3" "lightsalmon4" "lightseagreen"
[430] "lightskyblue" "lightskyblue1" "lightskyblue2"
[433] "lightskyblue3" "lightskyblue4" "lightslateblue"
[436] "lightslategray" "lightslategrey" "lightsteelblue"
[439] "lightsteelblue1" "lightsteelblue2" "lightsteelblue3"
[442] "lightsteelblue4" "lightyellow" "lightyellow1"
[445] "lightyellow2" "lightyellow3" "lightyellow4"
[448] "limegreen" "linen" "magenta"
[451] "magenta1" "magenta2" "magenta3"
[454] "magenta4" "maroon" "maroon1"
[457] "maroon2" "maroon3" "maroon4"
[460] "mediumaquamarine" "mediumblue" "mediumorchid"
[463] "mediumorchid1" "mediumorchid2" "mediumorchid3"
[466] "mediumorchid4" "mediumpurple" "mediumpurple1"
[469] "mediumpurple2" "mediumpurple3" "mediumpurple4"
[472] "mediumseagreen" "mediumslateblue" "mediumspringgreen"
[475] "mediumturquoise" "mediumvioletred" "midnightblue"
[478] "mintcream" "mistyrose" "mistyrose1"
[481] "mistyrose2" "mistyrose3" "mistyrose4"
[484] "moccasin" "navajowhite" "navajowhite1"
[487] "navajowhite2" "navajowhite3" "navajowhite4"
[490] "navy" "navyblue" "oldlace"
[493] "olivedrab" "olivedrab1" "olivedrab2"
[496] "olivedrab3" "olivedrab4" "orange"
[499] "orange1" "orange2" "orange3"
[502] "orange4" "orangered" "orangered1"
[505] "orangered2" "orangered3" "orangered4"
[508] "orchid" "orchid1" "orchid2"
[511] "orchid3" "orchid4" "palegoldenrod"
[514] "palegreen" "palegreen1" "palegreen2"
[517] "palegreen3" "palegreen4" "paleturquoise"
[520] "paleturquoise1" "paleturquoise2" "paleturquoise3"
[523] "paleturquoise4" "palevioletred" "palevioletred1"
[526] "palevioletred2" "palevioletred3" "palevioletred4"
[529] "papayawhip" "peachpuff" "peachpuff1"
[532] "peachpuff2" "peachpuff3" "peachpuff4"
[535] "peru" "pink" "pink1"
[538] "pink2" "pink3" "pink4"
[541] "plum" "plum1" "plum2"
[544] "plum3" "plum4" "powderblue"
[547] "purple" "purple1" "purple2"
[550] "purple3" "purple4" "red"
[553] "red1" "red2" "red3"
[556] "red4" "rosybrown" "rosybrown1"
[559] "rosybrown2" "rosybrown3" "rosybrown4"
[562] "royalblue" "royalblue1" "royalblue2"
[565] "royalblue3" "royalblue4" "saddlebrown"
[568] "salmon" "salmon1" "salmon2"
[571] "salmon3" "salmon4" "sandybrown"
[574] "seagreen" "seagreen1" "seagreen2"
[577] "seagreen3" "seagreen4" "seashell"
[580] "seashell1" "seashell2" "seashell3"
[583] "seashell4" "sienna" "sienna1"
[586] "sienna2" "sienna3" "sienna4"
[589] "skyblue" "skyblue1" "skyblue2"
[592] "skyblue3" "skyblue4" "slateblue"
[595] "slateblue1" "slateblue2" "slateblue3"
[598] "slateblue4" "slategray" "slategray1"
[601] "slategray2" "slategray3" "slategray4"
[604] "slategrey" "snow" "snow1"
[607] "snow2" "snow3" "snow4"
[610] "springgreen" "springgreen1" "springgreen2"
[613] "springgreen3" "springgreen4" "steelblue"
[616] "steelblue1" "steelblue2" "steelblue3"
[619] "steelblue4" "tan" "tan1"
[622] "tan2" "tan3" "tan4"
[625] "thistle" "thistle1" "thistle2"
[628] "thistle3" "thistle4" "tomato"
[631] "tomato1" "tomato2" "tomato3"
[634] "tomato4" "turquoise" "turquoise1"
[637] "turquoise2" "turquoise3" "turquoise4"
[640] "violet" "violetred" "violetred1"
[643] "violetred2" "violetred3" "violetred4"
[646] "wheat" "wheat1" "wheat2"
[649] "wheat3" "wheat4" "whitesmoke"
[652] "yellow" "yellow1" "yellow2"
[655] "yellow3" "yellow4" "yellowgreen"
In programmation, colors can also be specified with HEX numerotation
For instance: #2E86AB equals to steelblue
You can easily find hex color codes in google.
Finally, you can also use the rgb notation (red, green, blue)
In R, the rgb() function enables yu to set the correct amont of each primary color.
Sometimes one figure cannot show everything clearly.
We can combine several plots into one layout.
For this, the patchwork or ggarrange packages are very useful.
When you have different plots assigned to different objects, the patchwork package can combine them simply
ggplot2 has a function called ggsave with which yu can save your plot to a file.
First you generate the plot, then you can use the ggsave to save it into a fil so that you have:
Anther way is to use the png()function before the plot.
Then you generate the plot and run the dev.off() function.
For each figure, ask:
ggplot2 builds plots step by step.